package com.hy.example.common.api.result;

import com.querydsl.core.QueryResults;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import org.springframework.data.domain.Page;
import org.springframework.util.CollectionUtils;

import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;

@Getter
public class ApiPageResult<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    @Schema(description = "分页内容")
    private final List<T> content;
    @Schema(description = "总页数")
    private final int totalPage;
    @Schema(description = "总行数")
    private final long totalSize;

    private ApiPageResult() {
        this(Collections.emptyList(), 0, 0);
    }

    public ApiPageResult(List<? extends T> content, int totalPage, long totalSize) {
        this.content = Collections.unmodifiableList(content == null ? new ArrayList<>() : content);
        this.totalPage = Math.max(totalPage, 0);
        this.totalSize = totalSize < 0 ? 0 : totalSize;
    }

    public <U> ApiPageResult<U> map(Function<? super T, ? extends U> converter) {
        if (this == EMPTY || CollectionUtils.isEmpty(content)) {
            return (ApiPageResult<U>) this;
        }
        return new ApiPageResult<>(content.stream().map(converter).toList(),
                totalPage, totalSize);
    }

    @SuppressWarnings("rawtypes")
    private static final ApiPageResult EMPTY = new ApiPageResult<>();

    @SuppressWarnings("unchecked")
    public static <T> ApiPageResult<T> empty() {
        return EMPTY;
    }


    public static <T, U> ApiPageResult<U> with(Page<T> page, Function<? super T, ? extends U> converter) {
        return with(page.map(converter));
    }

    public static <T, U> ApiPageResult<U> with(QueryResults<T> results, Function<? super T, ? extends U> converter) {
        return with(results).map(converter);
    }

    public static <T> ApiPageResult<T> with(QueryResults<T> results) {
        return new ApiPageResult<>(results.getResults(), (int) Math.ceil((double) results.getTotal()
                / (double) results.getLimit()), results.getTotal());
    }

    public static <T> ApiPageResult<T> with(Page<T> page) {
        return new ApiPageResult<>(page.getContent(), page.getTotalPages(), page.getTotalElements());
    }

    /**
     * 内存手工分页
     *
     * @param rawContent 原始数据
     * @param pageNumber 分页号
     * @param pageSize   分页大小
     * @return 子集合
     */
    public static <T> ApiPageResult<T> memoryGenerate(List<? extends T> rawContent, int pageNumber, int pageSize) {
        if (rawContent == null || rawContent.isEmpty()) {
            return ApiPageResult.empty();
        }
        if (pageNumber == 0 && pageSize >= rawContent.size()) {
            return new ApiPageResult<>(rawContent, 1, rawContent.size());
        }
        if (pageNumber < 0) {
            pageNumber = 0;
        }
        if (pageSize < 1) {
            pageSize = 1;
        }
        if (rawContent.size() <= pageSize) {
            return new ApiPageResult<>(rawContent, 1, rawContent.size());
        }
        int indexPos = pageNumber * pageSize;
        int toIndex = indexPos + pageSize;
        if (toIndex > rawContent.size()) {
            toIndex = rawContent.size();
        }
        List<T> content = new ArrayList<>(rawContent.subList(indexPos, toIndex));
        int totalPage = (int) Math.ceil((double) rawContent.size() / (double) pageSize);
        return new ApiPageResult<>(content, totalPage, rawContent.size());
    }

    /**
     * 内存手工分页(尾部倒序)
     *
     * @param rawContent 原始数据
     * @param pageNumber 分页号
     * @param pageSize   分页大小
     * @return 子集合
     */
    public static <T> ApiPageResult<T> memoryGenerateRevert(List<? extends T> rawContent, int pageNumber, int pageSize) {
        if (rawContent == null || rawContent.isEmpty()) {
            return ApiPageResult.empty();
        }
        if (pageNumber < 0) {
            pageNumber = 0;
        }
        if (pageSize < 1) {
            pageSize = 1;
        }
        int totalSize = rawContent.size();
        if (totalSize <= pageSize) {
            return new ApiPageResult<>(rawContent, 1, totalSize);
        }
        int indexPos = pageNumber * pageSize;
        if (indexPos >= totalSize) {
            indexPos = totalSize;
        }
        int toIndex = indexPos + pageSize;
        if (toIndex > totalSize) {
            toIndex = totalSize;
        }
        List<T> content = new ArrayList<>(rawContent.subList(totalSize - toIndex, totalSize - indexPos));
        Collections.reverse(content);
        int totalPage = (int) Math.ceil((double) totalSize / (double) pageSize);
        return new ApiPageResult<>(content, totalPage, totalSize);

    }
}